/*
 * Packet.c
 *
 *  Created on: Jul 7, 2009
 *      Author: lfor
 *
 *
 *      Protocol description:
 *      The protocol is an exchange of "packets" over a reliable connection.
 *      The protocol is human readable.
 *      The protocol specify fields.
 *      A field is a line starting with a label (name of the field) separated from
 *      its value by a colon (:) and a space ( ), and finishing by end of line (LF).
 *      ex:
 *      Field: value LF
 *      The value can be any human readable string. It can be a single value or a list
 *      of values. A list of values is a comma separated list (value1, value2, ...).
 *      "" can be used to impose a string to be an unsplittable value, if the value contains a
 *      comma (,) for instance.
 *
 *      The protocol specifies six fields, always in the following order, with their values:
 *
 *      Status: [CALL | RECALL] | [OK | ERROR | ONGOING] | LST | Q - Mandatory
 *      Function: functionName - Mandatory, string
 *      Param: param1, param2, ... - Mandatory, comma separated list of parameters
 *      Return: return1, return2, ... - Optional, comma separated list of returns
 *      Error: error string - Optional, string
 *      Dlength: data length - optional, integer
 *
 *      If Dlength is specified all the rest of the packet is binary data that can be used freely
 *
 *      Caller packets:
 *      CALL - used to call a function in the cally (concatenation of callee and callby)
 *      RECALL - Used to call again a function if the cally specified ONGOING last time
 *
 *      Cally packets:
 *      OK - The cally answer to a CALL, must contain the Return field then.
 *      ERROR - The cally answer if there is an error back, MUST contain an Error field
 *      ONGOING - The cally answer if the function call is ongoing. Caller must RECALL later.
 *
 *      Lsit packet:
 *      LST - to list the registered function
 *
 *
 */

// FIXME - Packet: ParseBuffer corrupts the return value -> reproducible by checking errors when no error are returned

#include "Packet.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>


int ParseBuffer(char * Buffer, int Size, PacketType * Packet)
{
	PacketType RetPacket;
	RetPacket.Status = NONE;
	RetPacket.Function[0] = 0;
	RetPacket.Params[0] = 0;
	RetPacket.Returns[0] = 0;
	RetPacket.DataLength = 0;
	RetPacket.Data = NULL;

	if ((Buffer == NULL) || (Packet == NULL) || (Size <= 0))
	{
		return(PACKET_INVALID_PARAM);
	}

	int Index = 0;
	int Loop = 1;
	int Retval = PACKET_OK;

	while ((Index < Size) && (Loop == 1))
	{
		char FirstChar = Buffer[Index];

		// We use the first char to fasten the process
		switch (FirstChar)
		{
		case 'S':
			// Get Status
			#define FIELD "Status: "
			#define FIELD_LENGTH 8
			if (strncmp(FIELD, Buffer+Index, FIELD_LENGTH) == 0)
			{
				Index += FIELD_LENGTH;

				// Search end of line
				int i = 0;
				while (Buffer[Index+i] != '\n')
				{
					i++;
				}
				Buffer[Index+i] = 0;

				// Get value (we use the number of letter to fasten the process)
				switch (i)
				{
				case 1: // CLOSE
					if (strncmp("Q", Buffer+Index, i) == 0)
					{
						RetPacket.Status = CLOSE;
					}
					break;

				case 2: // OK
					if (strncmp("OK", Buffer+Index, i) == 0)
					{
						RetPacket.Status = OK;
					}
					break;

				case 3: // LST
					if (strncmp("LST", Buffer+Index, i) == 0)
					{
						RetPacket.Status = LIST;
					}
					break;

				case 4: // CALL
					if (strncmp("CALL", Buffer+Index, i) == 0)
					{
						RetPacket.Status = CALL;
					}
					break;

				case 5: // ERROR
					if (strncmp("ERROR", Buffer+Index, i) == 0)
					{
						RetPacket.Status = ERROR;
					}
					break;

				case 6: // RECALL
					if (strncmp("RECALL", Buffer+Index, i) == 0)
					{
						RetPacket.Status = RECALL;
					}
					break;

				case 7: // ONGOING
					if (strncmp("ONGOING", Buffer+Index, i) == 0)
					{
						RetPacket.Status = ONGOING;
					}
					break;

				default:
					RetPacket.Status = NONE;
					break;
				}

				if (RetPacket.Status == NONE)
				{
					Retval = PACKET_ERROR;
					Loop = 0;
				}

//				if (RetPacket.Status == LST)
//				{
//					Retval = PACKET_OK;
//					Loop = 0;
//				}

				Index += i+1;
			}
			else
			{
				Retval = PACKET_ERROR;
				Loop = 0;
			}
			#undef FIELD
			#undef FIELD_LENGTH
			break;

		case 'F':
			// Get Function
			#define FIELD "Function: "
			#define FIELD_LENGTH 10
			if (strncmp(FIELD, Buffer+Index, FIELD_LENGTH) == 0)
			{
				Index += FIELD_LENGTH;

				// Search end of line
				int i = 0;
				while (Buffer[Index+i] != '\n')
				{
					i++;
				}
				Buffer[Index+i] = 0;

				// Get value
				if ((i > 0) && (i <= MAX_NAME_SIZE))
				{
					strncpy(RetPacket.Function, Buffer+Index, i);
					RetPacket.Function[i] = 0;
				}
				else
				{
					RetPacket.Function[0] = 0;
				}

				Index += i+1;
			}
			else
			{
				Retval = PACKET_ERROR;
				Loop = 0;
			}
			#undef FIELD
			#undef FIELD_LENGTH
			break;

		case 'P':
			// Get Param
			#define FIELD "Param: "
			#define FIELD_LENGTH 7
			if (strncmp(FIELD, Buffer+Index, FIELD_LENGTH) == 0)
			{
				Index += FIELD_LENGTH;

				// Search end of line
				int i = 0;
				while (Buffer[Index+i] != '\n')
				{
					i++;
				}
				Buffer[Index+i] = 0;

				// Get value
				if ((i >= 0) && (i <= MAX_PARAM_SIZE))
				{
					strncpy(RetPacket.Params, Buffer+Index, i);
					RetPacket.Params[i] = 0;
				}
				else
				{
					RetPacket.Params[0] = 0;
				}

				Index += i+1;
			}
			else
			{
				Retval = PACKET_ERROR;
				Loop = 0;
			}
			#undef FIELD
			#undef FIELD_LENGTH
			break;

		case 'R':
			// Get Return
			#define FIELD "Return: "
			#define FIELD_LENGTH 8
			if (strncmp(FIELD, Buffer+Index, FIELD_LENGTH) == 0)
			{
				Index += FIELD_LENGTH;

				// Search end of line
				int i = 0;
				while (Buffer[Index+i] != '\n')
				{
					i++;
				}
				Buffer[Index+i] = 0;

				// Get value
				if ((i >= 0) && (i <= MAX_RETURN_SIZE))
				{
					strncpy(RetPacket.Returns, Buffer+Index, i);
					RetPacket.Returns[i] = 0;
				}
				else
				{
					RetPacket.Returns[0] = 0;
				}

				Index += i+1;
			}
			else
			{
				Retval = PACKET_ERROR;
				Loop = 0;
			}
			#undef FIELD
			#undef FIELD_LENGTH
			break;

		case 'E':
			// Get Error
			#define FIELD "Error: "
			#define FIELD_LENGTH 7
			if (strncmp(FIELD, Buffer+Index, FIELD_LENGTH) == 0)
			{
				Index += FIELD_LENGTH;

				// Search end of line
				int i = 0;
				while (Buffer[Index+i] != '\n')
				{
					i++;
				}
				Buffer[Index+i] = 0;

				// Get value
				if ((i >= 0) && (i <= MAX_ERROR_SIZE))
				{
					strncpy(RetPacket.Errors, Buffer+Index, i);
					RetPacket.Errors[i] = 0;
				}
				else
				{
					RetPacket.Errors[0] = 0;
				}

				Index += i+1;
			}
			else
			{
				Retval = PACKET_ERROR;
				Loop = 0;
			}
			#undef FIELD
			#undef FIELD_LENGTH
			break;

		case 'D':
			// Get Dlength
			#define FIELD "Dlength: "
			#define FIELD_LENGTH 9
			if (strncmp(FIELD, Buffer+Index, FIELD_LENGTH) == 0)
			{
				Index += FIELD_LENGTH;

				// Search end of line
				int i = 0;
				while (Buffer[Index+i] != '\n')
				{
					i++;
				}
				Buffer[Index+i] = 0;

				// Get value
				if ((i > 0) && (i <= MAX_NAME_SIZE))
				{
					RetPacket.DataLength = atoi(Buffer+Index);
				}
				else
				{
					RetPacket.DataLength = 0;
				}

				Index += i+1;

				if (RetPacket.DataLength != 0)
				{
					RetPacket.Data = Buffer+Index;
				}
				else
				{
					RetPacket.Data = NULL;
				}

				Loop = 0; // always the last field!!!!
			}
			else
			{
				Retval = PACKET_ERROR;
				Loop = 0;
			}
			#undef FIELD
			#undef FIELD_LENGTH
			break;

		default:
			Loop = 0;
			break;
		}
	}

	(*Packet).Status = RetPacket.Status;
	strncpy((*Packet).Function, RetPacket.Function, MAX_NAME_SIZE);
	strncpy((*Packet).Params, RetPacket.Params, MAX_PARAM_SIZE);
	strncpy((*Packet).Returns, RetPacket.Returns, MAX_RETURN_SIZE);
	strncpy((*Packet).Errors, RetPacket.Errors, MAX_ERROR_SIZE);
	(*Packet).DataLength = RetPacket.DataLength;
	(*Packet).Data = RetPacket.Data;

	return(Retval);
}


int FillBuffer(PacketType Packet, char * Buffer, int * Size)
{
	int Retval = PACKET_OK;
	char * TmpStr = NULL;

	if ((Buffer == NULL) || (Size == NULL))
	{
		return(PACKET_INVALID_PARAM);
	}

	if (*Size <= 0)
	{
		return(PACKET_INVALID_PARAM);
	}

	int MaxSize = *Size;

	switch (Packet.Status)
	{
	case CLOSE:
		TmpStr = "Q";
		break;
	case OK:
		TmpStr = "OK";
		break;
	case LIST:
		TmpStr = "LST";
		break;
	case CALL:
		TmpStr = "CALL";
		break;
	case ERROR:
		TmpStr = "ERROR";
		break;
	case RECALL:
		TmpStr = "RECALL";
		break;
	case ONGOING:
		TmpStr = "ONGOING";
		break;
	default:
		Retval = PACKET_ERROR;
		break;
	}

	if (Retval == PACKET_OK)
	{
		int Index = 0;

		Index += snprintf(Buffer, MaxSize, "Status: %s\nFunction: %s\nParam: %s\nReturn: %s\nError: %s\nDlength: %i\n", TmpStr, Packet.Function, Packet.Params, Packet.Returns, Packet.Errors, Packet.DataLength);

		if (Index < MaxSize)
		{
			if (Packet.DataLength != 0)
			{
				if (Packet.DataLength <= (MaxSize-Index))
				{
					if (Packet.Data != NULL)
					{
						bcopy((void *)Packet.Data, (void *)(Buffer+Index), Packet.DataLength);
						Index += Packet.DataLength;
					}
					else
					{
						Retval = PACKET_INVALID_PARAM;
					}
				}
				else
				{
					Retval = PACKET_NOT_ENOUGH_BUFFER;
				}
			}
		}
		else
		{
			Retval = PACKET_NOT_ENOUGH_BUFFER;
		}

		*Size = Index;
	}


	return(Retval);
}
